// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/http/http_basic_stream.h"

#include <memory>

#include "net/http/http_request_info.h"
#include "net/http/http_response_body_drainer.h"
#include "net/http/http_stream_parser.h"
#include "net/socket/client_socket_handle.h"

namespace net {

HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection,
    bool using_proxy)
    : state_(connection, using_proxy)
{
}

HttpBasicStream::~HttpBasicStream() { }

int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info,
    RequestPriority priority,
    const BoundNetLog& net_log,
    const CompletionCallback& callback)
{
    state_.Initialize(request_info, priority, net_log, callback);
    return OK;
}

int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers,
    HttpResponseInfo* response,
    const CompletionCallback& callback)
{
#if NET_DEBUG_MODE
    // Coomy LOG.
    NET_LOG_STREAM("### Basic Request Headers: ###");
    NET_LOG_STREAM("    %s # HTTP/1.1", state_.GenerateRequestLine().c_str());
    HttpRequestHeaders::Iterator it(headers);
    while (it.GetNext()) {
        std::string name = it.name();
        std::string value = it.value();
        NET_LOG_STREAM("    %s: %s", name.c_str(), value.c_str());
    }
    NET_LOG_STREAM("### Request Headers End ###");
#endif

    DCHECK(parser());
    return parser()->SendRequest(
        state_.GenerateRequestLine(), headers, response, callback);
}

UploadProgress HttpBasicStream::GetUploadProgress() const
{
    return parser()->GetUploadProgress();
}

int HttpBasicStream::ReadResponseHeaders(const CompletionCallback& callback)
{
    return parser()->ReadResponseHeaders(callback);
}

int HttpBasicStream::ReadResponseBody(IOBuffer* buf,
    int buf_len,
    const CompletionCallback& callback)
{
    return parser()->ReadResponseBody(buf, buf_len, callback);
}

void HttpBasicStream::Close(bool not_reusable)
{
    parser()->Close(not_reusable);
}

HttpStream* HttpBasicStream::RenewStreamForAuth()
{
    DCHECK(IsResponseBodyComplete());
    DCHECK(!parser()->IsMoreDataBuffered());
    // The HttpStreamParser object still has a pointer to the connection. Just to
    // be extra-sure it doesn't touch the connection again, delete it here rather
    // than leaving it until the destructor is called.
    state_.DeleteParser();
    return new HttpBasicStream(state_.ReleaseConnection().release(),
        state_.using_proxy());
}

bool HttpBasicStream::IsResponseBodyComplete() const
{
    return parser()->IsResponseBodyComplete();
}

bool HttpBasicStream::IsConnectionReused() const
{
    return parser()->IsConnectionReused();
}

void HttpBasicStream::SetConnectionReused() { parser()->SetConnectionReused(); }

bool HttpBasicStream::CanReuseConnection() const
{
    return parser()->CanReuseConnection();
}

int64_t HttpBasicStream::GetTotalReceivedBytes() const
{
    if (parser())
        return parser()->received_bytes();
    return 0;
}

int64_t HttpBasicStream::GetTotalSentBytes() const
{
    if (parser())
        return parser()->sent_bytes();
    return 0;
}

bool HttpBasicStream::GetLoadTimingInfo(
    LoadTimingInfo* load_timing_info) const
{
    return state_.connection()->GetLoadTimingInfo(IsConnectionReused(),
        load_timing_info);
}

void HttpBasicStream::GetSSLInfo(SSLInfo* ssl_info)
{
    parser()->GetSSLInfo(ssl_info);
}

void HttpBasicStream::GetSSLCertRequestInfo(
    SSLCertRequestInfo* cert_request_info)
{
    parser()->GetSSLCertRequestInfo(cert_request_info);
}

bool HttpBasicStream::GetRemoteEndpoint(IPEndPoint* endpoint)
{
    if (!state_.connection() || !state_.connection()->socket())
        return false;

    return state_.connection()->socket()->GetPeerAddress(endpoint) == OK;
}

Error HttpBasicStream::GetSignedEKMForTokenBinding(crypto::ECPrivateKey* key,
    std::vector<uint8_t>* out)
{
    return parser()->GetSignedEKMForTokenBinding(key, out);
}

void HttpBasicStream::Drain(HttpNetworkSession* session)
{
    HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(this);
    drainer->Start(session);
    // |drainer| will delete itself.
}

void HttpBasicStream::PopulateNetErrorDetails(NetErrorDetails* details)
{
    // TODO(mmenke):  Consumers don't actually care about HTTP version, but seems
    // like the right version should be reported, if headers were received.
    details->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP1_1;
    return;
}

void HttpBasicStream::SetPriority(RequestPriority priority)
{
    // TODO(akalin): Plumb this through to |connection_|.
}

} // namespace net
